use egui::Ui;
use serde::{Deserialize, Serialize};
use std::{
    collections::HashMap,
    ops::{Index as IndexOps, IndexMut},
};
use thunderdome::{
    iter::{Iter, IterMut},
    Arena, Index,
};
use uuid::Uuid;
use QcMacros::{external, Comp, Component};

use crate::{
    component::{Grid, Panel},
    core::{context::UiContext, layer::Layer, uiBind::UiBind},
    message::UiMessageType,
};

use super::UiNode;

#[derive(Debug, Comp, Deserialize, Serialize)]
#[external]
pub struct Canvas {
    inner: ComponentInner,
    pub pool: Arena<UiNode>,
    pub uiBindList: HashMap<Uuid, Vec<UiBind>>,
    pub children: Vec<Index>,
}

impl IndexOps<Index> for Canvas {
    type Output = UiNode;

    fn index(&self, index: Index) -> &Self::Output {
        &self.pool[index]
    }
}
impl IndexMut<Index> for Canvas {
    fn index_mut(&mut self, index: Index) -> &mut Self::Output {
        &mut self.pool[index]
    }
}

impl Canvas {
    pub fn new() -> Self {
        Canvas {
            inner: ComponentInner::default(),
            pool: Default::default(),
            uiBindList: Default::default(),
            children: Vec::new(),
        }
    }

    pub fn addUiBind(&mut self, comp: Uuid, bind: UiBind) {
        self.uiBindList.entry(comp).or_insert(vec![]).push(bind);
    }

    pub fn remove_ui_bind(&mut self, comp: Uuid, msg_type: UiMessageType) {
        self.uiBindList
            .entry(comp)
            .and_modify(|vec| vec.retain(|b| b.get_msg_type().ne(&msg_type)));
    }

    pub fn add_ui_bind_list(&mut self, comp: Uuid, mut bind: Vec<UiBind>) {
        self.uiBindList
            .entry(comp)
            .or_insert(vec![])
            .append(&mut bind);
    }

    pub fn getUiBind(&self, comp: Uuid) -> Option<&Vec<UiBind>> {
        self.uiBindList.get(&comp)
    }

    pub fn iter(&self) -> Iter<'_, UiNode> {
        self.pool.iter()
    }

    pub fn iter_mut(&mut self) -> IterMut<'_, UiNode> {
        self.pool.iter_mut()
    }

    pub fn clear(&mut self) {
        self.pool.clear();
    }

    // 更新子元素的坐标
    pub fn update(&mut self) {
        let mut hashmap = HashMap::new();
        for node in self.pool.iter() {
            let widget = node.1.get_widget();

            let mut world_position = widget.position;
            let mut parent = widget.parent;

            while let Some(index) = parent {
                if let Some(parent_widget) = self.pool.get(index) {
                    let parent_widget = parent_widget.get_widget();

                    world_position += parent_widget.position.to_vec2();
                    parent = parent_widget.parent;
                } else {
                    parent = None;
                }
            }

            hashmap.insert(node.0, world_position);
        }

        for node in self.pool.iter_mut() {
            let widget = node.1.get_widget_mut();

            if let Some(position) = hashmap.get(&node.0) {
                widget.set_world_position(*position);
            }
        }
    }

    pub fn insert(&mut self, node: UiNode) -> Index {
        let index = self.pool.insert(node);

        let child = self.pool[index].get_widget_mut();
        child.set_id(index);

        index
    }

    pub fn add_child_by_id(&mut self, index: Index) {
        self.children.push(index);
    }

    pub fn add_child(&mut self, node: UiNode) -> Index {
        let index = self.pool.insert(node);

        let child = self.pool[index].get_widget_mut();
        child.set_id(index);

        self.children.push(index);
        index
    }

    pub fn remove_child(&mut self, index: Index) -> Index {
        let child = self.pool[index].get_widget();

        let parent = child.parent;
        if let Some(parent) = parent {
            let parent = self.pool[parent].get_widget_mut();
            parent.remove_child(index);
        } else {
            self.children.retain(|id| *id != index);
        }

        let child = self.pool[index].get_widget_mut();
        child.set_parent(None);

        index
    }

    pub fn add_child_with_parent(&mut self, child: UiNode, parent: Option<Index>) -> Index {
        let index = self.pool.insert(child);

        let child = self.pool[index].get_widget_mut();

        child.set_id(index);
        child.set_parent(parent);

        if let Some(parent) = parent {
            let parent_node = &mut self.pool[parent];
            let parent_type_name = parent_node.typetag_name();
            let parent_widget = parent_node.get_widget_mut();
            parent_widget.add_child(index);

            let child = self.pool[index].get_widget_mut();
            if parent_type_name == Panel::typeName() {
                child.set_layer(Layer::Panel);
            } else if parent_type_name == Grid::typeName() {
                child.set_layer(Layer::Grid);
            }
        } else {
        }

        index
    }

    pub fn add_child_with_parent_by_id(&mut self, index: Index, parent: Option<Index>) -> Index {
        let child = self.pool[index].get_widget_mut();

        child.set_id(index);
        child.set_parent(parent);

        if let Some(parent) = parent {
            let parent_node = &mut self.pool[parent];
            let parent_type_name = parent_node.typetag_name();
            let parent_widget = parent_node.get_widget_mut();
            parent_widget.add_child(index);

            let child = self.pool[index].get_widget_mut();
            if parent_type_name == Panel::typeName() {
                child.set_layer(Layer::Panel);
            } else if parent_type_name == Grid::typeName() {
                child.set_layer(Layer::Grid);
            }
        } else {
        }

        index
    }

    pub fn remove_child_with_parent(&mut self, child: Index, parent: Option<Index>) {
        let child_widget = self.pool[child].get_widget_mut();
        child_widget.set_parent(None);

        if let Some(parent) = parent {
            let parent_widget = self.pool[parent].get_widget_mut();
            parent_widget.remove_child(child)
        } else {
            self.children.retain(|id| *id != child)
        }
    }

    pub fn remove(&mut self, child: Index) -> Option<UiNode> {
        self.pool.remove(child)
    }

    pub fn get_ui_node_by_name(&self, name: &str) -> Option<(Index, &UiNode)> {
        self.pool
            .iter()
            .find(|node| node.1.get_widget().name == name)
    }

    pub fn get(&self, index: Index) -> Option<&UiNode> {
        self.pool.get(index)
    }

    pub fn get_mut(&mut self, index: Index) -> Option<&mut UiNode> {
        self.pool.get_mut(index)
    }
}
